/*
 * dailyrollingfilelogger.cpp
 *
 *  Created on: Jan 10, 2012
 *      Author: sihai
 */
#include <time.h>
#include <pthread.h>
#include "log4cpp.h"
#include "dailyrollingfilelogger.h"
using namespace std;

DailyRollingFileLogger::DailyRollingFileLogger(const char* logfile, int buffersize)
{
	assert(logfile != NULL);
	assert(buffersize >= 0);
	this->logfile = (char*)malloc(strlen(logfile) + 1);
	strcpy(this->logfile, logfile);
	this->fd = -1;
	this->buffer = NULL;
	this->buffersize = buffersize;
	this->position = 0;
}

DailyRollingFileLogger::~DailyRollingFileLogger()
{
	// 先尝试flush
	if(this->fd >= 0 && this->buffer != NULL)
	{
		// 这里是析构函数,就不枷锁了吧
		_flush();
	}

	if(this->logfile != NULL)
	{
		free(this->logfile);
	}

	if(this->buffer != NULL)
	{
		free(this->buffer);
	}

	pthread_mutex_destroy(&this->mutex);

	if(this->lastchangedtime != NULL)
	{
		free(this->lastchangedtime);
	}

	if(this->fd != -1)
	{
		close(this->fd);
	}
}

bool DailyRollingFileLogger::init()
{
	fd = open(this->logfile, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
	if(fd == -1)
	{
		cout<<"[FATAL] DailyRollingFileLogger open log file:"<<this->logfile<<" failed"<<endl;
		return false;
	}

	if(this->buffersize > 0)
	{
		buffer = (char*)malloc(this->buffersize);
		if(buffer == NULL)
		{
			cout<<"[FATAL] DailyRollingFileLogger alloc buffer failed, try to alloc "<<this->buffersize<<" bytes memory"<<endl;
			return false;
		}

		if(pthread_mutex_init(&this->mutex, NULL))
		{
			cout<<"[FATAL] DailyRollingFileLogger init lock:mutex failed"<<" failed"<<endl;
			return false;
		}

		this->position = 0;
	}

	lastchangedtime = new tm();
	changed();
	return true;
}

void DailyRollingFileLogger::log(const char* msg)
{
	assert(fd >= 0);
	// 先尝试切换log file
	if(is_need_change_log_file())
	{
		//cout<<"Try to change log file"<<endl;
		change_log_file();
	}
	/*else
	{
		cout<<"Not need change log file"<<endl;
	}*/

	// 些日志
	int size = strlen(msg) + 2;
	char buffer[size];
	memset(buffer, 0x00, size);
	memcpy(buffer, msg, strlen(msg));
	buffer[size - 2] = '\n';
	buffer[size - 1] = 0x00;
	append(buffer);
}

bool DailyRollingFileLogger::is_need_change_log_file()
{
	time_t now = time(NULL);
	struct tm* tmptime = localtime(&now);
	if(lastchangedtime->tm_yday != tmptime->tm_yday)
	{
		return true;
	}

	return false;
}

bool DailyRollingFileLogger::change_log_file()
{
	pthread_mutex_lock(&this->mutex);
	// 先刷新缓存
	_flush();
	// 先关闭当前的log file
	close(this->fd);
	int size = strlen(this->logfile) + 128 + 1;
	char buffer[size];
	memset(buffer, 0x00, size);
	memcpy(buffer, this->logfile, strlen(this->logfile));
	strftime(buffer + strlen(this->logfile), 128, "-%Y-%m-%d", this->lastchangedtime);
	// 重命名老的log file
	//cout<<"Try to change log file from:"<<this->logfile<<" to:"<<buffer<<endl;
	rename(this->logfile, buffer);
	close(fd);
	fd = open(this->logfile, O_WRONLY | O_CREAT | O_APPEND);
	if(fd == -1)
	{
		pthread_mutex_unlock(&this->mutex);
		return false;
	}
	changed();
	pthread_mutex_unlock(&this->mutex);
	return true;
}

void DailyRollingFileLogger::changed()
{
	time_t now = time(NULL);
	struct tm* tmptime = localtime(&now);
	lastchangedtime->tm_sec = tmptime->tm_sec;
	lastchangedtime->tm_min = tmptime->tm_min;
	lastchangedtime->tm_hour = tmptime->tm_hour;
	lastchangedtime->tm_mday = tmptime->tm_mday;
	lastchangedtime->tm_mon = tmptime->tm_mon;
	lastchangedtime->tm_year = tmptime->tm_year;
	lastchangedtime->tm_wday = tmptime->tm_wday;
	lastchangedtime->tm_yday = tmptime->tm_yday;
	lastchangedtime->tm_isdst = tmptime->tm_isdst;
}

void DailyRollingFileLogger::append(const char* msg)
{
	int length = strlen(msg);

	if(this->buffer == NULL)
	{
		// 无缓存,直接写文件
		write(fd, msg, length);
	}
	else
	{
		// 写入buffer,先取得缓存的锁
		pthread_mutex_lock(&this->mutex);
		if(this->buffersize - this->position < length)
		{
			// buffer 不够,需要flush,释放缓存
			_flush();
		}
		else
		{
			// buffer够用
			memcpy(this->buffer, msg, length);
			this->position += length;
		}
		// 释放缓存的锁
		pthread_mutex_unlock(&this->mutex);
	}
}

void DailyRollingFileLogger::_flush()
{
	cout<<"[INFO] Flush log buffer"<<endl;
	write(this->fd, this->buffer, this->position + 1);
	memset(this->buffer, 0x00, this->buffersize);
	this->position = 0;
}
